Skip to content

chore: address ProcessRunner review comments from PR #89#91

Merged
orpiske merged 2 commits intowanaku-ai:mainfrom
orpiske:quick-fix/process-runner-review-fixes
Mar 3, 2026
Merged

chore: address ProcessRunner review comments from PR #89#91
orpiske merged 2 commits intowanaku-ai:mainfrom
orpiske:quick-fix/process-runner-review-fixes

Conversation

@orpiske
Copy link
Copy Markdown
Contributor

@orpiske orpiske commented Mar 3, 2026

Summary

  • Use redirectErrorStream(true) to merge stderr into stdout, preventing deadlock when subprocess writes heavily to stderr
  • Use try-with-resources for BufferedReader in readOutput to prevent resource leaks
  • Restore interrupt status via Thread.currentThread().interrupt() before rethrowing WanakuException on InterruptedException
  • Make tests cross-platform with OS detection helper
  • Add test for non-zero exit status (verifies no exception is thrown)
  • Add test for merged stderr/stdout stream behavior

Test plan

  • mvn verify passes
  • All three ProcessRunnerTest tests pass

Summary by Sourcery

Improve ProcessRunner robustness and portability while expanding its test coverage.

Bug Fixes:

  • Prevent potential deadlock and lost error output by merging the subprocess stderr into stdout in ProcessRunner.
  • Preserve thread interrupt status when handling InterruptedException in ProcessRunner run methods.

Enhancements:

  • Use try-with-resources when reading process output to avoid leaking IO resources in ProcessRunner.

Tests:

  • Make ProcessRunner tests OS-agnostic by using platform-specific command invocations.
  • Add tests to verify non-zero exit codes do not throw exceptions and that stderr is captured via the merged output stream.

- Use redirectErrorStream(true) to prevent stderr deadlock
- Use try-with-resources for BufferedReader in readOutput
- Restore interrupt status before rethrowing WanakuException
- Make tests cross-platform with OS detection
- Add test for non-zero exit status behavior
- Add test for merged stderr/stdout stream
@sourcery-ai
Copy link
Copy Markdown

sourcery-ai bot commented Mar 3, 2026

Reviewer's Guide

Refactors ProcessRunner to merge stderr into stdout, improve resource and interrupt handling, and extends tests to be cross‑platform while covering non‑zero exit codes and merged stderr/stdout behavior.

Sequence diagram for ProcessRunner.runWithOutput with merged stderr/stdout and interrupt handling

sequenceDiagram
    actor Caller
    participant ProcessRunner
    participant ProcessBuilder
    participant Process
    participant BufferedReader
    participant WanakuException

    Caller->>ProcessRunner: runWithOutput(command)
    activate ProcessRunner
    ProcessRunner->>ProcessBuilder: new ProcessBuilder(command)
    ProcessRunner->>ProcessBuilder: redirectOutput(PIPE)
    ProcessRunner->>ProcessBuilder: redirectErrorStream(true)
    ProcessRunner->>ProcessBuilder: start()
    ProcessBuilder-->>ProcessRunner: Process
    activate Process

    ProcessRunner->>Process: getInputStream()
    ProcessRunner->>BufferedReader: create with InputStreamReader
    activate BufferedReader

    loop read lines
        BufferedReader->>ProcessRunner: readLine()
        alt line != null
            ProcessRunner->>ProcessRunner: append line to output
        else line == null
            ProcessRunner->>ProcessRunner: break
        end
    end
    BufferedReader-->>ProcessRunner: close() (try-with-resources)
    deactivate BufferedReader

    ProcessRunner->>Process: waitFor()
    Process-->>ProcessRunner: exitCode
    deactivate Process

    ProcessRunner-->>Caller: output String
    deactivate ProcessRunner

    %% Error path: IOException
    Caller->>ProcessRunner: runWithOutput(command) (I/O error)
    activate ProcessRunner
    ProcessRunner->>WanakuException: new WanakuException(IOException)
    ProcessRunner-->>Caller: throw WanakuException
    deactivate ProcessRunner

    %% Error path: InterruptedException
    Caller->>ProcessRunner: runWithOutput(command) (Interrupted)
    activate ProcessRunner
    ProcessRunner->>ProcessRunner: Thread.currentThread().interrupt()
    ProcessRunner->>WanakuException: new WanakuException(InterruptedException)
    ProcessRunner-->>Caller: throw WanakuException
    deactivate ProcessRunner
Loading

Updated class diagram for ProcessRunner with stderr/stdout merge and resource handling

classDiagram
    class ProcessRunner {
        - ProcessRunner()
        + static String runWithOutput(String[] command)
        - static String readOutput(Process process)
        + static void run(File directory, String[] command)
        + static void run(File directory, Map environmentVariables, String[] command)
    }

    class WanakuException {
        + WanakuException(Throwable cause)
    }

    class ProcessBuilder {
        + ProcessBuilder(String[] command)
        + ProcessBuilder redirectOutput(Redirect redirect)
        + ProcessBuilder redirectErrorStream(boolean redirect)
        + Process start()
    }

    class Process {
        + InputStream getInputStream()
        + int waitFor()
    }

    class BufferedReader {
        + BufferedReader(Reader in)
        + String readLine()
        + void close()
    }

    class Thread {
        + static void currentThread_interrupt()
    }

    class Logger {
        + void info(String message)
        + void error(String message, String details, Throwable throwable)
    }

    ProcessRunner ..> WanakuException : throws
    ProcessRunner ..> ProcessBuilder : uses
    ProcessRunner ..> Process : uses
    ProcessRunner ..> BufferedReader : uses
    ProcessRunner ..> Thread : restores_interrupt
    ProcessRunner ..> Logger : logs
    ProcessBuilder ..> Process : creates
    Process ..> BufferedReader : provides_InputStream
    Thread <|.. Thread : currentThread_interrupt

    class Redirect
    class File
    class Map
    class InputStream
    class Reader

    ProcessBuilder ..> Redirect : uses
    Process ..> InputStream : returns
    BufferedReader ..> Reader : wraps
    ProcessRunner ..> File : uses
    ProcessRunner ..> Map : uses
Loading

File-Level Changes

Change Details Files
Merge subprocess stderr into stdout to avoid deadlocks and simplify output consumption.
  • Replace explicit stderr redirection with redirectErrorStream(true) on ProcessBuilder so that stderr is merged into stdout
  • Keep stdout redirected to a PIPE so readOutput consumes both streams from a single InputStream
capabilities-common/src/main/java/ai/wanaku/capabilities/sdk/common/ProcessRunner.java
Improve robustness of process output reading and interrupt handling.
  • Wrap BufferedReader over process.getInputStream() in a try-with-resources to ensure the stream is closed after reading
  • Preserve thread interrupt status by calling Thread.currentThread().interrupt() in InterruptedException handlers before wrapping in WanakuException
capabilities-common/src/main/java/ai/wanaku/capabilities/sdk/common/ProcessRunner.java
Make ProcessRunner tests OS-agnostic and broaden behavioral coverage.
  • Introduce a small isWindows() helper to detect the OS in tests
  • Change existing echo-based test to invoke platform-appropriate shell (cmd.exe / sh) so it runs on both Windows and Unix-like systems
  • Add a test verifying that non-zero subprocess exit codes do not cause runWithOutput to throw and still return output
  • Add a test verifying that stderr content is captured via the merged stdout/stderr stream
capabilities-common/src/test/java/ai/wanaku/capabilities/sdk/common/ProcessRunnerTest.java

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

Copy link
Copy Markdown

@sourcery-ai sourcery-ai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey - I've found 2 issues, and left some high level feedback:

  • In readOutput, consider explicitly specifying a charset for InputStreamReader (e.g., StandardCharsets.UTF_8) instead of relying on the platform default to avoid surprises across different environments.
  • The Windows/non-Windows command selection logic in the tests is duplicated across multiple test methods; consider extracting a small helper (e.g., runEchoCommand / runCommand(String unixCmd, String windowsCmd)) to keep the tests more concise and avoid repetition.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- In `readOutput`, consider explicitly specifying a charset for `InputStreamReader` (e.g., `StandardCharsets.UTF_8`) instead of relying on the platform default to avoid surprises across different environments.
- The Windows/non-Windows command selection logic in the tests is duplicated across multiple test methods; consider extracting a small helper (e.g., `runEchoCommand` / `runCommand(String unixCmd, String windowsCmd)`) to keep the tests more concise and avoid repetition.

## Individual Comments

### Comment 1
<location path="capabilities-common/src/main/java/ai/wanaku/capabilities/sdk/common/ProcessRunner.java" line_range="45" />
<code_context>
-        String line;
-        while ((line = reader.readLine()) != null) {
-            output.append(line).append("\n");
+        try (BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()))) {
+            StringBuilder output = new StringBuilder();
+            String line;
</code_context>
<issue_to_address>
**suggestion (bug_risk):** Consider specifying an explicit charset when reading process output.

`InputStreamReader` without a charset uses the platform default, which may differ from the subprocess encoding and vary between environments. Please pass an explicit charset (e.g., `new InputStreamReader(process.getInputStream(), StandardCharsets.UTF_8)`) to ensure consistent decoding across platforms.

Suggested implementation:

```java
import java.io.InputStreamReader;
import java.nio.charset.StandardCharsets;

```

```java
        try (BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream(), StandardCharsets.UTF_8))) {

```
</issue_to_address>

### Comment 2
<location path="capabilities-common/src/test/java/ai/wanaku/capabilities/sdk/common/ProcessRunnerTest.java" line_range="11-13" />
<code_context>

 public class ProcessRunnerTest {

+    private static boolean isWindows() {
+        return System.getProperty("os.name").toLowerCase().contains("win");
+    }
+
     @Test
</code_context>
<issue_to_address>
**suggestion:** Consider extracting OS-specific command construction to a helper to avoid duplication across tests

`runWithOutput_returnsCommandOutput`, `runWithOutput_nonZeroExitDoesNotThrow`, and `runWithOutput_capturesStderrViaMergedStream` all duplicate the `if (isWindows()) { cmd.exe ... } else { sh ... }` logic. A small helper like `private static String[] shellCommand(String script)` that returns the OS‑specific command array would centralize this logic so future changes to OS detection or shell invocation only need to be made in one place.

Suggested implementation:

```java
    private static boolean isWindows() {
        return System.getProperty("os.name").toLowerCase().contains("win");
    }

    private static String[] shellCommand(String script) {
        if (isWindows()) {
            return new String[]{"cmd.exe", "/c", script};
        } else {
            return new String[]{"sh", "-c", script};
        }
    }

```

```java
    @Test
    void runWithOutput_returnsCommandOutput() {
        String[] command = shellCommand("echo hello");
        String output = ProcessRunner.runWithOutput(command);

        assertNotNull(output, "Output should not be null");

```

There are likely similar OS-specific command constructions in the tests `runWithOutput_nonZeroExitDoesNotThrow` and `runWithOutput_capturesStderrViaMergedStream`. For each of those tests, replace the duplicated `if (isWindows()) { cmd.exe ... } else { sh ... }` blocks by constructing the command using `shellCommand("<appropriate script>")` and then calling `ProcessRunner.runWithOutput(command)` (or the relevant ProcessRunner method) with the resulting array.
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

- Use StandardCharsets.UTF_8 in InputStreamReader for consistent decoding
- Extract shellCommand() helper in tests to remove OS-detection duplication
@orpiske orpiske merged commit 6a0186f into wanaku-ai:main Mar 3, 2026
5 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant